/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Communication Module                           */
/*                                                                           */
/*    Copyright (C) 2007 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    comRC5Receiver - RC5 standard infrared receiver                        */
/*                                                                           */
/*    Dependencies:                                                          */
/*      RC5IsSet()   - must be defined in drvIOConfig.h to read              */
/*                     receiver input pin                                    */
/*      drvRXGeneral - general receiver driver must be implemented           */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include "drvIOCOnfig.h"
#include <comRC5Receiver.h>
#include <drvRXGeneral.h>

///////////////////////////////////////////////////////////////////////////////
// Module Local Variables
static dosWord l_time_stamp = 0;							// temporary storage for timestamp
static dosWord l_half_bit_time = 0;
static dosWord l_data = 0;
static dosWord l_received_data = 0;
static dosBool l_data_ready = dosFalse;
static dosBool l_sync_edge = dosFalse;

// receive status
// 0 - waiting for the start bit
// 1 - start bit received, calibration started
// 2 - calibration ended, waiting for second start bit
// 3 - second start bit received, start reception
// 3..n - receiving bits
static dosByte l_status = 0;

///////////////////////////////////////////////////////////////////////////////
// User function prototypes
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Initialize
void comRC5Init(void)
{
	InitRC5RX();
}

///////////////////////////////////////////////////////////////////////////////
// Is Data Received
dosBool comRC5IsDataReceived(void)
{
	return l_data_ready;
}

///////////////////////////////////////////////////////////////////////////////
// Get Received Data
dosWord comRC5GetReceivedData(void)
{
	l_data_ready = dosFalse;

	return l_received_data;
}

///////////////////////////////////////////////////////////////////////////////
// PIN changed interrupt callback
void drvRXGENPinChangedCallback( dosWord in_time_stamp )
{
	dosWord time;

	// check status
	switch( l_status )
	{
		// start calibration at the first received edge
		case 0:
			drvRXGENSetTimerCallback( in_time_stamp + comRC5_HALF_BIT_LENGTH + comRC5_MAX_PULSE_DEVIATION );
			l_time_stamp = in_time_stamp;
			l_status++;
			l_sync_edge = dosFalse;
			break;

		// start bit closing edge
		case 1:
			// calculate bit time
			l_half_bit_time = in_time_stamp - l_time_stamp;

			if( l_half_bit_time < (comRC5_HALF_BIT_LENGTH - comRC5_MAX_PULSE_DEVIATION) || 
					l_half_bit_time > (comRC5_HALF_BIT_LENGTH + comRC5_MAX_PULSE_DEVIATION) )
			{
				// invalid start bit length - ignore transmission
				l_status = 0;
			}
			else
			{
				// start bit length accepted -> continue reception
				drvRXGENSetTimerCallback( in_time_stamp + comRC5_HALF_BIT_LENGTH + comRC5_MAX_PULSE_DEVIATION );
				l_status++;
			}
			break;

		// second start bit, start data reception
		case 2:
			// set sample point
			drvRXGENSetTimerCallback( in_time_stamp + 3 * l_half_bit_time / 2 );
			l_time_stamp = in_time_stamp;
			l_status++;
			break;

		// check sample point
		default:
			if( l_sync_edge )
			{
				time = ( in_time_stamp - l_time_stamp ) % l_half_bit_time; 
				l_sync_edge = dosFalse;

				if( time < comRC5_MAX_PULSE_DEVIATION ||
						time > comRC5_HALF_BIT_LENGTH - comRC5_MAX_PULSE_DEVIATION )
				{
					// syncedge accepted, prepare for sampling next bit
					drvRXGENSetTimerCallback( in_time_stamp + 3 * l_half_bit_time / 2 );
				}
				else
				{
					// syncedge is invalid, restart reception
					//l_status = 0;
				}
			}
			break;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Timer callback function
void drvRXGENTimerCallback(void)
{
	switch( l_status )
	{
		// no action
		case 0:
			break;

		// start bit longer than expected, cancel reception
		case 1:
			l_status = 0;
			break;

		// second start bit longer than expected, cancel reception
		case 2:
			l_status = 0;
			break;

		// first databit received
		case 3:
			l_data = RC5IsSet();

			// receive sync edge
			drvRXGENAddTimerCallback( 3 * l_half_bit_time / 2 + comRC5_MAX_PULSE_DEVIATION );
			l_sync_edge = dosTrue;

			l_status++;
			break;

		// data bit received
		default:
			// check if syncedge was received
			if( l_sync_edge )
			{
				// syncedge was not received, cancel reception
				l_status = 0;
				l_sync_edge = dosFalse;
			}
			else
			{
				// if last data bit received
				if( l_status == comRC5_DATA_BIT_COUNT + 3 )
				{
					// store received data and restart receiver
					l_status = 0;
					l_received_data = l_data;
					l_data_ready = dosTrue;
					l_sync_edge = dosFalse;
				}
				else
				{
					// get data
					l_data <<= 1;
					l_data |= RC5IsSet();

					// set timeout for sync eedge
					drvRXGENAddTimerCallback( 3 * l_half_bit_time / 2 + comRC5_MAX_PULSE_DEVIATION );
					l_sync_edge = dosTrue;
				
					// next bit to receive
					l_status++;
				}
			}
			break;
	}
}
